package cn.edu.tjufe.llp.tin.advice;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;

import org.springframework.beans.TypeMismatchException;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.util.StringUtils;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.multipart.support.MissingServletRequestPartException;
import org.springframework.web.servlet.NoHandlerFoundException;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;

import cn.edu.tjufe.llp.tin.dto.common.Rs;


@ControllerAdvice
public class CustomRestExceptionHandler extends ResponseEntityExceptionHandler {
	

	// 400
	/**
	 * 请求参数错误
	 */
	@Override
	protected ResponseEntity<Object> handleMethodArgumentNotValid(final MethodArgumentNotValidException ex,
			final HttpHeaders headers, final HttpStatus status, final WebRequest request) {
		logger.info(ex.getClass().getName());
		
		final List<String> errors = new ArrayList<String>();
		for (final FieldError error : ex.getBindingResult().getFieldErrors()) {
			errors.add(error.getField() + ": " + error.getDefaultMessage());
		}
		for (final ObjectError error : ex.getBindingResult().getGlobalErrors()) {
			errors.add(error.getObjectName() + ": " + error.getDefaultMessage());
		}
		// final ApiErrorVo apiError = new ApiErrorVo(HttpStatus.BAD_REQUEST, ex.getLocalizedMessage(), errors);
		final Rs<List<String>> apiError = Rs.fail(HttpStatus.BAD_REQUEST.value(), "输入数据异常.", errors);
		return handleExceptionInternal(ex, apiError, headers, HttpStatus.BAD_REQUEST, request);
	}

	/**
	 * 请求参数数据类型转换错误
	 */
	@Override
	protected ResponseEntity<Object> handleBindException(final BindException ex, final HttpHeaders headers,
			final HttpStatus status, final WebRequest request) {
		logger.info(ex.getClass().getName());
		
		final List<String> errors = new ArrayList<String>();
		for (final FieldError error : ex.getBindingResult().getFieldErrors()) {
			errors.add(error.getField() + ": " + error.getDefaultMessage());
		}
		for (final ObjectError error : ex.getBindingResult().getGlobalErrors()) {
			errors.add(error.getObjectName() + ": " + error.getDefaultMessage());
		}
		// final ApiErrorVo apiError = new ApiErrorVo(HttpStatus.BAD_REQUEST, ex.getLocalizedMessage(), errors);
		final Rs<List<String>> apiError = Rs.fail(HttpStatus.BAD_REQUEST.value(), "数据类型转换异常.", errors);
		return handleExceptionInternal(ex, apiError, headers, HttpStatus.BAD_REQUEST, request);
	}
	
	@Override
	protected ResponseEntity<Object> handleHttpMessageNotReadable(HttpMessageNotReadableException ex, HttpHeaders headers, HttpStatus status, WebRequest request){
		logger.info(ex.getClass().getName());

		final String error = ex.getRootCause().getMessage();

		final Rs<List<String>> apiError = Rs.fail(HttpStatus.BAD_REQUEST.value(), ex.getLocalizedMessage(), Arrays.asList(error));
		return new ResponseEntity<Object>(apiError, new HttpHeaders(), HttpStatus.BAD_REQUEST);
	}

	@Override
	protected ResponseEntity<Object> handleTypeMismatch(final TypeMismatchException ex, final HttpHeaders headers,
			final HttpStatus status, final WebRequest request) {
		logger.info(ex.getClass().getName());
		
		final String error = ex.getValue() + " value for " + ex.getPropertyName() + " should be of type "
				+ ex.getRequiredType();

		final Rs<List<String>> apiError = Rs.fail(HttpStatus.BAD_REQUEST.value(),  ex.getLocalizedMessage(), Arrays.asList(error));
		return new ResponseEntity<Object>(apiError, new HttpHeaders(), HttpStatus.BAD_REQUEST);
	}

	@Override
	protected ResponseEntity<Object> handleMissingServletRequestPart(final MissingServletRequestPartException ex,
			final HttpHeaders headers, final HttpStatus status, final WebRequest request) {
		logger.info(ex.getClass().getName());
		
		final String error = ex.getRequestPartName() + " part is missing";
		final Rs<List<String>> apiError = Rs.fail(HttpStatus.BAD_REQUEST.value(),  ex.getLocalizedMessage(), Arrays.asList(error));
		return new ResponseEntity<Object>(apiError, new HttpHeaders(), HttpStatus.BAD_REQUEST);
	}

	@Override
	protected ResponseEntity<Object> handleMissingServletRequestParameter(
			final MissingServletRequestParameterException ex, final HttpHeaders headers, final HttpStatus status,
			final WebRequest request) {
		logger.info(ex.getClass().getName());
		
		final String error = ex.getParameterName() + " parameter is missing";
		final Rs<List<String>> apiError = Rs.fail(HttpStatus.BAD_REQUEST.value(),  ex.getLocalizedMessage(), Arrays.asList(error));
		return new ResponseEntity<Object>(apiError, new HttpHeaders(), HttpStatus.BAD_REQUEST);
	}

	//

	@ExceptionHandler({ MethodArgumentTypeMismatchException.class })
	public ResponseEntity<Object> handleMethodArgumentTypeMismatch(final MethodArgumentTypeMismatchException ex,
			final WebRequest request) {
		logger.info(ex.getClass().getName());
		
		final String error = ex.getName() + " should be of type " + ex.getRequiredType().getName();

		final Rs<List<String>> apiError = Rs.fail(HttpStatus.BAD_REQUEST.value(),  ex.getLocalizedMessage(), Arrays.asList(error));
		return new ResponseEntity<Object>(apiError, new HttpHeaders(), HttpStatus.BAD_REQUEST);
	}

	@ExceptionHandler({ ConstraintViolationException.class })
	public ResponseEntity<Object> handleConstraintViolation(final ConstraintViolationException ex,
			final WebRequest request) {
		logger.info(ex.getClass().getName());
		
		final List<String> errors = new ArrayList<String>();
		for (final ConstraintViolation<?> violation : ex.getConstraintViolations()) {
//			errors.add(violation.getRootBeanClass().getName() + " " + violation.getPropertyPath() + ": "
//			+ violation.getMessage());
			errors.add(violation.getMessage());
		}

//		final Rs<List<String>> apiError = Rs.fail(HttpStatus.BAD_REQUEST.value(),  ex.getLocalizedMessage(), errors);
		final Rs<List<String>> apiError = Rs.fail(HttpStatus.BAD_REQUEST.value(), "输入错误", errors);
		return new ResponseEntity<Object>(apiError, new HttpHeaders(), HttpStatus.BAD_REQUEST);
	}

	// 404
	@Override
	protected ResponseEntity<Object> handleNoHandlerFoundException(final NoHandlerFoundException ex,
			final HttpHeaders headers, final HttpStatus status, final WebRequest request) {
		logger.info(ex.getClass().getName());
		//
		final String error = "No handler found for " + ex.getHttpMethod() + " " + ex.getRequestURL();

		final Rs<List<String>> apiError = Rs.fail(HttpStatus.NOT_FOUND.value(), ex.getLocalizedMessage(), Arrays.asList(error));
		return new ResponseEntity<Object>(apiError, new HttpHeaders(), HttpStatus.NOT_FOUND);
	}

	// 405

	@Override
	protected ResponseEntity<Object> handleHttpRequestMethodNotSupported(
			final HttpRequestMethodNotSupportedException ex, final HttpHeaders headers, final HttpStatus status,
			final WebRequest request) {
		logger.info(ex.getClass().getName());
		//
		final StringBuilder builder = new StringBuilder();
		builder.append(ex.getMethod());
		builder.append(" method is not supported for this request. Supported methods are ");
		ex.getSupportedHttpMethods().forEach(t -> builder.append(t + " "));

		final Rs<List<String>> apiError = Rs.fail(HttpStatus.METHOD_NOT_ALLOWED.value(), ex.getLocalizedMessage(),
				Arrays.asList(builder.toString()));
		return new ResponseEntity<Object>(apiError, new HttpHeaders(), HttpStatus.METHOD_NOT_ALLOWED);
	}

	// 415
	@Override
	protected ResponseEntity<Object> handleHttpMediaTypeNotSupported(final HttpMediaTypeNotSupportedException ex,
			final HttpHeaders headers, final HttpStatus status, final WebRequest request) {
		logger.info(ex.getClass().getName());
		//
		final StringBuilder builder = new StringBuilder();
		builder.append(ex.getContentType());
		builder.append(" media type is not supported. Supported media types are ");
		ex.getSupportedMediaTypes().forEach(t -> builder.append(t + " "));

		final Rs<List<String>> apiError = Rs.fail(HttpStatus.UNSUPPORTED_MEDIA_TYPE.value(), ex.getLocalizedMessage(),
				Arrays.asList(builder.substring(0, builder.length() - 2)));
		return new ResponseEntity<Object>(apiError, new HttpHeaders(), HttpStatus.UNSUPPORTED_MEDIA_TYPE);
	}

	// 500
	@ExceptionHandler({ Exception.class })
	public ResponseEntity<Object> handleAll(final Exception ex, final WebRequest request) {
		logger.info(ex.getClass().getName());
		logger.error("error", ex);
		
		final Rs<List<String>> apiError = Rs.fail(HttpStatus.INTERNAL_SERVER_ERROR.value(), ex.getLocalizedMessage());
		return new ResponseEntity<Object>(apiError, new HttpHeaders(), HttpStatus.INTERNAL_SERVER_ERROR.value());
	}
	
	// Mybatis + MySQL中出现唯一索引重复时，抛出的异常
    @ExceptionHandler(DuplicateKeyException.class)
    public ResponseEntity<Object> handleDuplicateKeyException(DuplicateKeyException e) {
        String msg = e.getMessage();
        Pattern p = Pattern.compile("for key \'(.*?)\'");
        Matcher m = p.matcher (msg);
        msg = "";
        if (m.find()) {
            String key = m.group();
            // key = StringUtils.removeStart(key, "for key '");
            key = StringUtils.delete(key, "for key '");
            // key = StringUtils.removeEnd(key, "'");
            key = StringUtils.delete(key, "'");
            msg = DuplicateKeyToMsg.duplicateKeyToMsg(key);
        }
        final Rs<String> apiError = Rs.fail(500, "重复记录。", msg);
        return new ResponseEntity<Object>(apiError, new HttpHeaders(), HttpStatus.INTERNAL_SERVER_ERROR.value());
    }
	
	// JPA + MySQL中出现唯一索引重复时，抛出的异常
    @ExceptionHandler(DataIntegrityViolationException.class)
    public ResponseEntity<Object> handleDataIntegrityViolationException(DataIntegrityViolationException e) {
        String msg = e.getMessage();
        Pattern p = Pattern.compile("constraint \\[(.*?)\\]");
        Matcher m = p.matcher(msg);
        msg = "";
        if (m.find()) {
            String key = m.group();
            // key = StringUtils.removeStart(key, "for key '");
            key = StringUtils.delete(key, "constraint [");
            // key = StringUtils.removeEnd(key, "'");
            key = StringUtils.delete(key, "]");
            msg = DuplicateKeyToMsg.duplicateKeyToMsg(key);
        }
        final Rs<String> apiError = Rs.fail(500, "重复记录。", msg);
        return new ResponseEntity<Object>(apiError, new HttpHeaders(), HttpStatus.INTERNAL_SERVER_ERROR.value());
    }
	
}
